class TimeLayout:
	key = ''
	times = []
	interval = 1
	
class Seria:
	name = ''
	timelayout = 0
	values = []

class wdml():
	winddir = ['N',       'NE',      'E',    'SE', 'S', 'SW', 'W', 'NW']
	windarr = ['&#8593;', '&#8599;', '&#8594;', '&#8600;', '&#8595;', '&#8601;', '&#8592;', '&#8598;']
	HOURS = 24*7
	
	def dump(o):
		for property, value in inspect.getmembers(o):
#			print(property, ": ", len(value))
			print(property)

	def getTimeLayouts(dom):
		timelayouts = {}
		starttime = datetime.max
		data = dom.firstChild.getElementsByTagName("data")[0]
		tlayouts = data.getElementsByTagName("time-layout")
		for tlayout in tlayouts:
			timelayout = TimeLayout()
			timelayout.key = tlayout.getElementsByTagName("layout-key")[0].firstChild.nodeValue
			timelayout.times = []
			m = re.search('k-p([0-9]+)h-', timelayout.key)
			timelayout.interval = int(m.group(1))

			times = tlayout.getElementsByTagName("start-valid-time")
			for t in times:
				timestr = t.firstChild.nodeValue[:19]
				tm = datetime.strptime(timestr, '%Y-%m-%dT%H:00:00')
				timelayout.times.append(tm)
				if tm < starttime:
					starttime = tm
			timelayouts[timelayout.key] = timelayout
		return timelayouts, starttime

	def getIntencity(intencity):
		if intencity == 'light':
			return 'LT'
		if intencity == 'very light':
			return 'lt'
		if intencity == 'moderate':
			return 'MO'
		if intencity == 'heavy':
			return 'HV'
		return '??'

	def parseWeatherConditions(node, timelayout):
		series = []
		seriaWeatherType = Seria()
		seriaWeatherType.name = 'Weather Type'
		seriaWeatherType.timelayout = timelayout
		seriaWeatherType.values = []
		
		seriaVisibility = Seria()
		seriaVisibility.name = 'Visibility'
		seriaVisibility.timelayout = timelayout
		seriaVisibility.values = []
		
		seriaSnowIntensity = Seria()
		seriaSnowIntensity.name = 'Snow Intensity'
		seriaSnowIntensity.timelayout = timelayout
		seriaSnowIntensity.values = []
		
		seriaRainIntensity = Seria()
		seriaRainIntensity.name = 'Rain Intensity'
		seriaRainIntensity.timelayout = timelayout
		seriaRainIntensity.values = []
		
		conditions = node.getElementsByTagName('weather-conditions')
		for cond in conditions:
			values = cond.getElementsByTagName('value')
			visibility = ''
			snowint = ''
			rainint = ''
			for v in values:
				wt = v.getAttribute('weather-type')
				intencity = v.getAttribute('intensity')
				
				if wt == 'snow' or wt == 'snow showers':
					snowint = WdmlParser.getIntencity(intencity)
					
				if wt == 'rain' or wt == 'rain showers':
					rainint = WdmlParser.getIntencity(intencity)
					
				vnodes = v.getElementsByTagName('visibility')
				if len(vnodes) > 0:
					if vnodes[0].hasChildNodes():
						visibility = vnodes[0].firstChild.nodeValue
						
			seriaSnowIntensity.values.append(snowint)
			seriaRainIntensity.values.append(rainint)
			seriaWeatherType.values.append(' ')
			seriaVisibility.values.append(visibility)
		
		series.append(seriaSnowIntensity)
		series.append(seriaRainIntensity)
#			series.append(seriaWeatherType)
		series.append(seriaVisibility)
		return series

	def parseValues(node, timelayout):
		seria = Seria()
		name = node.getElementsByTagName("name")[0]
		seria.name = name.firstChild.nodeValue
		seria.timelayout = timelayout
		seria.values = []
		
		values = node.getElementsByTagName('value')
		for v in values:
			nv = ''
			if v.hasChildNodes():
				nv = v.firstChild.nodeValue

			if seria.name.find('Temperature') >= 0:
				nv = "%3d" % ((int(nv) - 32) * 5 / 9)
			if seria.name == 'Relative Humidity':
				nv = int(nv)
				if nv < 0:
					nv = ''
			if seria.name == 'Wind Direction':
				nv = WdmlParser.windarr[int(((360-int(nv)+22.5) % 360) / 45)]
			if seria.name == 'Snow Amount':
				if nv == '0':
					nv = ''
			seria.values.append(nv)
		return seria

	def download(url, fname):
		try:
			cache.download(url, fname, timeout = 30)
		except Exception as details:
			rt.log.error(details)
			if path.isfile(fname):
				os.remove(fname)
			raise

	def parseData(dom):
		timelayouts, starttime = WdmlParser.getTimeLayouts(dom)
			
		series = []
		parameters = dom.firstChild.getElementsByTagName("parameters")[0]
		for node in parameters.childNodes:
			if node.nodeType != dom.ELEMENT_NODE:
				continue
				
			name = node.getElementsByTagName("name")[0]
			timelayout = timelayouts[node.getAttribute('time-layout')]

			if name.firstChild.nodeValue == 'Weather Type, Coverage, and Intensity':
				series += WdmlParser.parseWeatherConditions(node, timelayout)
			else:
				seria = WdmlParser.parseValues(node, timelayout)
				if seria.name.find('Daily') == 0:
					continue
				series.append(seria)
		return series, starttime
		
	def parseHead(dom):
		head = dom.getElementsByTagName('head')[0]
		product = head.getElementsByTagName('product')[0]
		creationdate = product.getElementsByTagName('creation-date')[0]
		refreshfrequency = creationdate.getAttribute('refresh-frequency')
		return creationdate.firstChild.nodeValue, refreshfrequency

	def createTable(series, timestart):
		
		table = [['' for row in range(0, WdmlParser.HOURS + 1)] for i in range(0, len(series) + 2)]

		table[0][0] = 'Filled'
		table[1][0] = 'Day'
		table[2][0] = 'Hour'
		
		for i in range(2, WdmlParser.HOURS + 1):
			tm = timestart + timedelta(hours = i - 2)
			hour = ' ' + tm.strftime('%H')
			day = ' ' + tm.strftime('%a')[:2]
			
			table[0][i] = 0
			table[1][i] = day
			table[2][i] = hour
			
		rx = 3
		for s in series:
			if s.name == 'Watches, Warnings, and Advisories':
				continue
			
			table[rx][0] = s.name
			table[rx][1] = 0 # empty flag
			for cx in range(0, len(s.timelayout.times)):
				delta = s.timelayout.times[cx] - timestart
				d = delta.days * 24 + int(delta.seconds / 3600) + 1
				if d > 0 and d < WdmlParser.HOURS:
					if type(s.values[cx]) == int or len(s.values[cx]) > 0:
						table[rx][d] = s.values[cx]
						table[0][d] = 1
						table[rx][1] = 1
			rx += 1

		return table
		
	def printTable(table):
		for i in range(1, WdmlParser.HOURS):
			if table[0][i] == 1:
				print(fg(str(table[1][i]).rjust(3), 'red'), end = '')
		print('')
		
		for i in range(2, len(table)):
			row = table[i]
			if row[0] == 'Filled':
				continue
			print(row[0].ljust(40), end = '')
			for i in range(1, WdmlParser.HOURS):
				if table[0][i] == 1:
					print(str(row[i]).rjust(3), end = '')
			print('')
	
	def rowToHtml(table, rowNum, refRowNum, tdstyle = 'td'):
		
		if table[rowNum][1] == 0:
			return ''
		
		s = ''
		prevday = ''
		idx = 1
		
		style = ''
		if refRowNum != 0:
			style = tdstyle + '0'
		
		row = table[rowNum]
		refrow = table[refRowNum]
		t = td(row[0], 'left')
		for cx in range(2, WdmlParser.HOURS + 1):
			if table[0][cx] == 1:
				if refRowNum != 0 and prevday != str(refrow[cx]):
					idx += 1
					style = tdstyle + str(idx % 2)
				fgcolor = ''
				if row[0] == 'Temperature':
					if len(row[cx]) > 0 and  int(row[cx]) < 0:
						fgcolor = 'blue'
						
				t += td(str(row[cx]), style, fgcolor)
				prevday = str(refrow[cx])
				
		return tr(t)
	
	def tableToHtml(table):

		s = '<table>\n'
		s += WdmlParser.rowToHtml(table, 1, 1, 'th')
		s += WdmlParser.rowToHtml(table, 2, 1, 'th')
			
		for rx in range(3, len(table)):
			s += WdmlParser.rowToHtml(table, rx, 1, 'td')
		
		s += '</table>\n'

		return s
	
	def rowToText(table, rowNum, refRowNum, tdstyle = 'td'):
		
		if table[rowNum][1] == 0:
			return ''
		
		s = ''
		prevday = ''
		idx = 1
		
		style = ''
		if refRowNum != 0:
			style = tdstyle + '0'
		
		row = table[rowNum]
		refrow = table[refRowNum]
		t = row[0]
		
		if t == 'Day': t = 'DAY '
		if t == 'Temperature': t = 'TEMP'
		if t == 'Apparent Temperature': t = 'ATMP'
		if t == 'Wind Speed': t = 'WIND'
		if t == 'Wind Direction': t = 'WDIR'
		if t == 'Cloud Cover Amount': t = 'CLDS'
		if t == 'Relative Humidity': t = 'HUMY'
		if t == 'Rain Intensity': t = 'RAIN'
		if t == 'Visibility': t = 'VISB'

		if t == 'WDIR': return ''
		
		t += ' '
		
		color = ''
		for cx in range(2, WdmlParser.HOURS + 1):
			if table[0][cx] == 1:
			
				if prevday != str(refrow[cx]):
					idx += 1
					if row[0] == 'Apparent Temperature':
						color = ['cyan', 'magenta'][idx % 2] 
						
					if row[0] == 'Day':
						color = ['green', 'red'][idx % 2] 
					
				t += fg(str(row[cx]).rjust(3), color)

				prevday = str(refrow[cx])
				
		return t + '\n'
		
	def tableToText(table):

		s = ''
		s += WdmlParser.rowToText(table, 1, 1, 'th')
		s += WdmlParser.rowToText(table, 2, 1, 'th')
			
		for rx in range(3, len(table)):
			s += WdmlParser.rowToText(table, rx, 1, 'td')
		
		return s
		
	def isError(dom):
		errors = dom.getElementsByTagName("error")
		if len(errors) > 0:
			print(errors[0].nodeName)
			return True
		return False
	
	def getAddress(zip):
		godir = rt.settings['tmpdir'] + '/geocode/'
		if not os.path.exists(godir):
			os.mkdir(godir)
			
		url = geocode_url % zip
		fname = godir + '%s.xml' % zip
		
		if cache.download(url, fname, timeout = 365*24*60):
			rt.log.info(url + ' downloaded to ' + fname)
			
		f = open(fname, 'rt')
		s = f.read()
		f.close()
		
		return s
	
	def getForecastHtml(zip, output):
		url = forecast_url % zip
		fname = rt.settings['tmpdir'] + 'fcast%s.xml' % zip
		
		WdmlParser.download(url, fname + '.tmp')
		rt.log.info(url + ' downloaded to ' + fname + '.tmp')
		dom = parse(fname + '.tmp')
		
		if WdmlParser.isError(dom):
			os.remove(fname + '.tmp')
			rt.log.error(dom.toprettyxml())
			return ('<div>Forecast for zip code ' + zip + ' is not available</div>', False)
		
		shutil.copy(fname + '.tmp', fname)
		rt.log.info(fname + '.tmp copied to ' + fname)
		creationdate, refreshfrequency = WdmlParser.parseHead(dom)
		series, starttime = WdmlParser.parseData(dom)

		address = WdmlParser.getAddress(zip)
		
		table = WdmlParser.createTable(series, starttime)
		
		text = address
		if output == 'html':
			text = '<div>' + text + '</div>\n'
			text += WdmlParser.tableToHtml(table)
			text += '<br>'
		else:
			text += '\n'
			text += WdmlParser.tableToText(table)
			text = '\033[2J' + text + '\n'
		
		rt.log.info(zip + ' text generated')
		return (text, True)
	
	def processZips(req, hours, output):
		print(hours)
		WdmlParser.HOURS = hours
		zips = req.split('/')[3];
		body = ''
		for zip in zips.split(','):
			(text, hr) = WdmlParser.getForecastHtml(zip, output)
			if hr == False:
				return None
			body += text;
			
		if output == 'html':
			with open(rt.settings['bindir'] + '/page.html') as f:
				t = Template(f.read())
			d = dict(body = body, title = zips)
			body = t.safe_substitute(d)

		return body
